// PART OF THE MACHINE SIMULATION. DO NOT CHANGE. package nachos.security; import nachos.machine.*; import java.io.File; import java.security.Permission; import java.io.FilePermission; import java.util.PropertyPermission; import java.net.NetPermission; import java.awt.AWTPermission; import java.security.PrivilegedAction; import java.security.PrivilegedExceptionAction; import java.security.PrivilegedActionException; /** * Protects the environment from malicious Nachos code. */ public class NachosSecurityManager extends SecurityManager { /** * Allocate a new Nachos security manager. * * @param testDirectory the directory usable by the stub file system. */ public NachosSecurityManager(File testDirectory) { this.testDirectory = testDirectory; fullySecure = Config.getBoolean("NachosSecurityManager.fullySecure"); } /** * Return a privilege object for this security manager. This security * manager must not be the active security manager. * * @return a privilege object for this security manager. */ public Privilege getPrivilege() { Lib.assertTrue(this != System.getSecurityManager()); return new PrivilegeProvider(); } /** * Install this security manager. */ public void enable() { Lib.assertTrue(this != System.getSecurityManager()); doPrivileged(new Runnable() { public void run() { System.setSecurityManager(NachosSecurityManager.this); } }); } private class PrivilegeProvider extends Privilege { public void doPrivileged(Runnable action) { NachosSecurityManager.this.doPrivileged(action); } public Object doPrivileged(PrivilegedAction action) { return NachosSecurityManager.this.doPrivileged(action); } public Object doPrivileged(PrivilegedExceptionAction action) throws PrivilegedActionException { return NachosSecurityManager.this.doPrivileged(action); } public void exit(int exitStatus) { invokeExitNotificationHandlers(); NachosSecurityManager.this.exit(exitStatus); } } private void enablePrivilege() { if (privilegeCount == 0) { Lib.assertTrue(privileged == null); privileged = Thread.currentThread(); privilegeCount++; } else { Lib.assertTrue(privileged == Thread.currentThread()); privilegeCount++; } } private void rethrow(Throwable e) { disablePrivilege(); if (e instanceof RuntimeException) throw (RuntimeException) e; else if (e instanceof Error) throw (Error) e; else Lib.assertNotReached(); } private void disablePrivilege() { Lib.assertTrue(privileged != null && privilegeCount > 0); privilegeCount--; if (privilegeCount == 0) privileged = null; } private void forcePrivilege() { privileged = Thread.currentThread(); privilegeCount = 1; } private void exit(int exitStatus) { forcePrivilege(); System.exit(exitStatus); } private boolean isPrivileged() { // the autograder does not allow non-Nachos threads to be created, so.. if (!TCB.isNachosThread()) return true; return (privileged == Thread.currentThread()); } private void doPrivileged(final Runnable action) { doPrivileged(new PrivilegedAction() { public Object run() { action.run(); return null; } }); } private Object doPrivileged(PrivilegedAction action) { Object result = null; enablePrivilege(); try { result = action.run(); } catch (Throwable e) { rethrow(e); } disablePrivilege(); return result; } private Object doPrivileged(PrivilegedExceptionAction action) throws PrivilegedActionException { Object result = null; enablePrivilege(); try { result = action.run(); } catch (Exception e) { throw new PrivilegedActionException(e); } catch (Throwable e) { rethrow(e); } disablePrivilege(); return result; } private void no() { throw new SecurityException(); } private void no(Permission perm) { System.err.println("\n\nLacked permission: " + perm); throw new SecurityException(); } /** * Check the specified permission. Some operations are permissible while * not grading. These operations are regulated here. * * @param perm the permission to check. */ public void checkPermission(Permission perm) { String name = perm.getName(); // some permissions are strictly forbidden if (perm instanceof RuntimePermission) { // no creating class loaders if (name.equals("createClassLoader")) no(perm); } // allow the AWT mess when not grading if (!fullySecure) { if (perm instanceof NetPermission) { // might be needed to load awt stuff if (name.equals("specifyStreamHandler")) return; } if (perm instanceof RuntimePermission) { // might need to load libawt if (name.startsWith("loadLibrary.")) { String lib = name.substring("loadLibrary.".length()); if (lib.equals("awt")) { Lib.debug(dbgSecurity, "\tdynamically linking " + lib); return; } } } if (perm instanceof AWTPermission) { // permit AWT stuff if (name.equals("accessEventQueue")) return; } } // some are always allowed if (perm instanceof PropertyPermission) { // allowed to read properties if (perm.getActions().equals("read")) return; } // some require some more checking if (perm instanceof FilePermission) { if (perm.getActions().equals("read")) { // the test directory can only be read with privilege if (isPrivileged()) return; enablePrivilege(); // not allowed to read test directory directly w/out privilege try { File f = new File(name); if (f.isFile()) { File p = f.getParentFile(); if (p != null) { if (p.equals(testDirectory)) no(perm); } } } catch (Throwable e) { rethrow(e); } disablePrivilege(); return; } else if (perm.getActions().equals("write") || perm.getActions().equals("delete")) { // only allowed to write test diretory, and only with privilege verifyPrivilege(); try { File f = new File(name); if (f.isFile()) { File p = f.getParentFile(); if (p != null && p.equals(testDirectory)) return; } } catch (Throwable e) { no(perm); } } else if (perm.getActions().equals("execute")) { // only allowed to execute with privilege, and if there's a net verifyPrivilege(); if (Machine.networkLink() == null) no(perm); } else { no(perm); } } // default to requiring privilege verifyPrivilege(perm); } /** * Called by the <tt>java.lang.Thread</tt> constructor to determine a * thread group for a child thread of the current thread. The caller must * be privileged in order to successfully create the thread. * * @return a thread group for the new thread, or <tt>null</tt> to use the * current thread's thread group. */ public ThreadGroup getThreadGroup() { verifyPrivilege(); return null; } /** * Verify that the caller is privileged. */ public void verifyPrivilege() { if (!isPrivileged()) no(); } /** * Verify that the caller is privileged, so as to check the specified * permission. * * @param perm the permission being checked. */ public void verifyPrivilege(Permission perm) { if (!isPrivileged()) no(perm); } private File testDirectory; private boolean fullySecure; private Thread privileged = null; private int privilegeCount = 0; private static final char dbgSecurity = 'S'; }